package clustering;

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
import graphNew.MainGraph;
import gui.AgentChooser;

import java.awt.Dimension;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;

import javax.swing.JFrame;

import main.ProgramOptions;
import main.Translating;
import nl.uu.cs.treewidth.algorithm.TreewidthDP;

import org.jgrapht.graph.DefaultWeightedEdge;

import components.Agent;
import components.Operator;

/*
 * the purpose of the class is to run a clustering algorithm on a given graph,
 * and to generate appropriate output file
 */
public class ClusterAlgo {
	//FIELDS
	
	//field i,j = 0 iff there is no edge between agent i to j. and equals num iff number of edges from agent i to agent j = num.
	private int agentsEdgesMatrix[][];
	private VoltageCluster voltageCluster;
	private EdgeBetwennessCluster EBcluster;
	private Set<Agent> agents;
	private MainGraph mainGraph;
	
	//CONSTRUCTORS
	public ClusterAlgo(int _num_candidates){
		this.voltageCluster = new VoltageCluster(_num_candidates);
		this.EBcluster = new EdgeBetwennessCluster(ProgramOptions.edgesToRemove);
		this.agents = new HashSet<Agent>();		
	}
	
	//METHODS
	
	//clusters according to VoltageClusterer algorithm
	public void cluster(MainGraph graph){
		this.mainGraph = graph;
		Set<VerticesSet> clusters = voltageCluster.cluster(graph, voltageCluster.getNum_candidates());
		
		for(VerticesSet vSet : clusters){
			this.agents.add(generateAgent(vSet));
		}
		
		updateAgentsEdgesMatrix();
		
		for (Agent agent : this.agents)
			agent.putAgentId();
	}
	
	//clusters according to EdgeBetwennessClusterer algorithm
	public void edgeBetwennesCluster(MainGraph graph){
		this.mainGraph = graph;
		Set<VerticesSet> clusters = this.EBcluster.cluster(graph, 0);	//the second param doesn't matter
		
		for(VerticesSet vSet : clusters){
			this.agents.add(generateAgent(vSet));
		}
		
		updateAgentsEdgesMatrix();
		
		for (Agent agent : this.agents)
			agent.putAgentId();
	}
	
	
	
	//outputs to the screen the graph of agents got from clustering
	public void showClusterAgentsGraph(){
		Graph<Agent, ClusterWeightedEdge> jungAgentGraph = new DirectedSparseGraph<Agent, ClusterWeightedEdge>();
		for(Agent agent : this.agents)
			jungAgentGraph.addVertex(agent);
		
		int numAgents = this.agents.size();
		for(int i=0; i<numAgents; i++){
			for(int j=0; j<numAgents; j++){
				if(i!=j && this.agentsEdgesMatrix[i][j] > 0){
					ClusterWeightedEdge edge = new ClusterWeightedEdge(this.agentsEdgesMatrix[i][j]);
					jungAgentGraph.addEdge(edge, getAgentById(i), getAgentById(j));	//agent ids start from 0
				}
			}
		}						
		
		// The Layout<V, E> is parameterized by the vertex and edge types
		Layout<Agent, ClusterWeightedEdge> layout = new FRLayout<Agent, ClusterWeightedEdge>(jungAgentGraph);
		layout.setSize(new Dimension(300,300)); // sets the initial size of the space
		
		// The BasicVisualizationServer<V,E> is parameterized by the edge types
		VisualizationViewer<Agent, ClusterWeightedEdge> vv =
		new VisualizationViewer<Agent, ClusterWeightedEdge>(layout);
		vv.setPreferredSize(new Dimension(400,400)); //Sets the viewing area size		
				
		//Vertices and edges view editing		
		vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<Agent>());
		vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<ClusterWeightedEdge>());
		vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);
		
		//iterative mouse for ZOOM IN/OUT
		DefaultModalGraphMouse<Agent, ClusterWeightedEdge> gm = 
			new DefaultModalGraphMouse<Agent, ClusterWeightedEdge>();
		gm.setMode(ModalGraphMouse.Mode.TRANSFORMING);
		vv.setGraphMouse(gm);
		
		//graphic output / visualization
		JFrame frame = new JFrame("FULL CAGENTS GRAPH");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().add(vv);
		frame.pack();
		frame.setVisible(true);
		
		//activating tree width algorithm
		TreewidthDP	tw=new TreewidthDP();
		//tw.setInput(Translating.ourGraphToListGraph(Translating.jungGraphToOurGraph(jungAgentGraph)));		//jungGraphToListGraph(jungAgentGraph));
		int x=-1;
		try{
			tw.setInput(Translating.jungGraphToListGraph(jungAgentGraph));		
			tw.run();
			x = tw.getTreewidth();
		}
		catch(Exception e){}
		finally{
			AgentChooser.main(x, agents);
		}	
		
	}
	
	
	
	
	//getter
	public Agent getAgentById(int id){
		for(Agent agent : this.agents)
			if (agent.getAgentId() == id)
				return agent;
		return null;
	}
	
	
	
	//gets set of vertices and returns a new agent built out of the vertices in input
	public Agent generateAgent(VerticesSet vSet){
		Agent agent;
		
		Vector<Operator> operators = new Vector<Operator>();
		for(Operator op : vSet.getNodesSet())
			operators.add(op);
						
		MainGraph agentGraph = new MainGraph(operators);
		agent = new Agent(agentGraph, 0);	//the  0 (rank) should be set after!
		return agent;
	}
	
	
	//updates the agentsEdgesMatrix. in the field i,j of the matrix there is a number which represents
	//the amount of edges encoded in one Agent Graph edge.
	public void updateAgentsEdgesMatrix(){
		int matrixSize = this.agents.size();
		this.agentsEdgesMatrix = new int[matrixSize][matrixSize];
		for(int i=0; i<matrixSize; i++)
			for(int j=0; j<matrixSize; j++)
				this.agentsEdgesMatrix[i][j] = 0;
		
		try {
			Set<DefaultWeightedEdge> allEdges = this.mainGraph.getMainGraph().edgeSet();
			for(DefaultWeightedEdge edge : allEdges){
				int source = this.mainGraph.getMainGraph().getEdgeSource(edge).getAgentId();
				int target= this.mainGraph.getMainGraph().getEdgeTarget(edge).getAgentId();
				if (source>=0 && target>=0){	//checking that the source and target both belong to some agents
					this.agentsEdgesMatrix[source][target]++;
				}
				else	throw new Exception("source or target doesn't belong to any agent");
			}
			
			
		} catch (Exception e) {			
			e.printStackTrace();
		}
	}
	

	
}
